package ro.pub.cs.hwchecker.comm.client.json;

import java.util.Map;
import java.util.Map.Entry;

import ro.pub.cs.hwchecker.comm.client.req.CheckerCallback;
import ro.pub.cs.hwchecker.comm.client.req.CheckerRequest;
import ro.pub.cs.hwchecker.comm.client.req.CheckerRequestException;

import com.google.gwt.http.client.Request;
import com.google.gwt.http.client.RequestBuilder;
import com.google.gwt.http.client.RequestCallback;
import com.google.gwt.http.client.RequestException;
import com.google.gwt.http.client.Response;
import com.google.gwt.http.client.URL;
import com.google.gwt.json.client.JSONException;
import com.google.gwt.json.client.JSONParser;
import com.google.gwt.json.client.JSONValue;
import com.google.gwt.core.client.GWT; 

public abstract class JSONRequestBuilder {
	protected class JSONCheckerRequest implements CheckerRequest {
		public Request innerRequest = null;
		
		public JSONCheckerRequest() {
			
		}
		
		public JSONCheckerRequest(Request req) {
			this.innerRequest = req;
		}

		public void cancel() {
			innerRequest.cancel();
		}

		public boolean isPending() {
			return innerRequest.isPending();
		}
		
	}
	
	protected String buildQueryString(String[] keys, String[] values) {
		StringBuffer sb = new StringBuffer();
		
		for (int i = 0; i < keys.length; i++) {
			if (i > 0) {
				sb.append("&");
			}
			
			sb.append(URL.encodeComponent(keys[i]));
			sb.append("=");
			sb.append(URL.encodeComponent(values[i]));
		}
		
		return sb.toString();
	}
	
	protected String buildQueryString(Map<String, String> entries) {
		StringBuffer sb = new StringBuffer();
		boolean first = true;
		
		for (Entry<String, String> entry: entries.entrySet()) {
			if (!first) {
				sb.append("&");
			} else {
				first = false;
			}
			sb.append(URL.encodeComponent(entry.getKey()));
			sb.append("=");
			sb.append(URL.encodeComponent(entry.getValue()));
		}
		
		return sb.toString();
	}
	
	protected CheckerRequestException parseErrorCode(Response response) 
	{
		String codeStr = response.getHeader("X-HWChecker-ErrorCode");
		String codeMsg = response.getHeader("X-HWChecker-ErrorMsg");
		int code = CheckerRequestException.CODE_OK;
		
		if (codeStr != null && codeStr.length() > 0) 
		{
			try {
				code = Integer.parseInt(codeStr);
			} catch (NumberFormatException e) {
				return CheckerRequestException.errorInvalidRequest(e);
			}
		}
		
		if (code != CheckerRequestException.CODE_OK)
			return new CheckerRequestException(code, codeMsg);
		
		// No error occurred
		return null;
	}
	
	protected void printHeaders(Response resp)
	{
		GWT.log(resp.getHeadersAsString(), null); 
	}
	
	protected CheckerRequest performRequest(String url, String postData,
			final CheckerCallback<JSONValue> callback) throws CheckerRequestException {
		
		RequestBuilder rb = new RequestBuilder(RequestBuilder.POST,
				url);
		
		// Ensure proper recognition of our data format
		rb.setHeader("Content-Type", "application/x-www-form-urlencoded"); 
		try {
			// This will be the token used to track the future connection
			final JSONCheckerRequest checkerReq = new JSONCheckerRequest();
			
			Request r = rb.sendRequest(postData, new RequestCallback() {
			
				public void onResponseReceived(Request request, Response response) 
				{ 
					if (response.getStatusCode() != Response.SC_OK) {
						// An error occurred
						GWT.log(response.getText(), null); 
						callback.onCheckerError(checkerReq, 
								CheckerRequestException.errorNotConnected(null));
					} else {
						printHeaders(response); 
						// The connection seems allright, let's parse the content
						CheckerRequestException error = parseErrorCode(response);
						JSONValue respValue = null;
						
						if (error == null) { // No exception pending
							try {
								if (response.getText().length() > 0)
									respValue = JSONParser.parse(response.getText());
								
								callback.onCheckerResponse(checkerReq, respValue);
							} catch (JSONException e) {
								GWT.log("JSON Error", e); 
								error = CheckerRequestException.errorInvalidRequest(e);
								callback.onCheckerError(checkerReq, error);
							}
						} else {
							callback.onCheckerError(checkerReq, error);
						}
					}
			
				}
			
				public void onError(Request request, Throwable exception) {
					// Propagate the error
					GWT.log("Send error", exception); 
					callback.onCheckerError(checkerReq, 
							new CheckerRequestException(exception));
			
				}
			});
			
			// Assign the inner request to our object handle, so
			// the user could manipulate the connection
			checkerReq.innerRequest = r;
			
			return checkerReq;
		} catch (RequestException e) {
			GWT.log("Send failed", e); 
			throw CheckerRequestException.errorNotConnected(e);
		}
	}
}
